from SimPy.Simulation import *
## from SimPy.SimulationTrace import *
from random import expovariate,seed
import random as ran

class Task(Process):
    """ A computer  task  requires at least
    one use of the CPU and possibly accesses to a
    disk drive."""
    completed = 0
    def execute(self):
        start_time = now()
        # while Task.completed < maxCompletions:
        while now()-start_time<60:        
            self.debug(" starts thinking")
            thinktime = ran.expovariate(1.0/MeanThinkTime)
            yield hold,self,thinktime
            self.debug(" request cpu")
            yield request,self,cpu
            self.debug(" got cpu")
            CPUtime=ran.expovariate(1.0/MeanCPUTime)
            yield hold,self,CPUtime
            yield release,self,cpu
            self.debug(str(Task.completed)+" finish cpu")
            Task.completed += 1
        self.debug(" completed %d tasks"%(Task.completed,))
        

    def debug(self,message):
        FMT="%9.3f %s %s"
        if DEBUG:
            print FMT%(now(),self.name,message)


class Clerk(Process):
    def work(self,myOffice):
        #start_time = now()
        #while now()-start_time<60: 
        yield request,self,myOffice,100 #leave desk
        # print now(),[x.name for x in myOffice.activeQ]
        yield hold,self,samplingIntrl
        yield release,self,myOffice #return to desk

class Generator(Process):        
    def execute(self,numberOfUsers,samplingIntrl):
        completedBefore=0
        timeBefore=now()
        for j in range(len(numberOfUsers)):            
            # configuring number of servers
            # e.g. for 2 resource we need 20-2 occupiers
            occupiers=maxServers-numberOfServers[j]
            for i in range(occupiers):
                c=Clerk('Clerk %s'%i)
                activate(c,c.work(cpu))            

#            for i in range(4):
#                c=Clerk('Clerk %s'%i)
#                activate(c,c.work(cpu))            


            # configuring number of customers
            for i in range(numberOfUsers[j]):
                t = Task(name="Job "+'i')
                activate(t,t.execute())
                
            # waiting 60 secs
            yield hold,self,samplingIntrl                         
            completed_cur = (Task.completed - completedBefore)
            throughput_ = completed_cur/(now()-timeBefore)
            # self.debug("interval "+str(now()/samplingIntrl)+ ",["+str(timeBefore)+","+ str(now()) +"]"+
            #           " completed "+str(completed_cur) + " throughput "+str(throughput_))
            completed.append(completed_cur) 
            throughput.append(throughput_)
                                    
            completedBefore = Task.completed
            timeBefore=now()          


    def debug(self,message):
        FMT="%9.3f %s %s"
        #if DEBUG:
        print FMT%(now(),self.name,message)

    
## Experiment data -------------------------

# configs
ran.seed(111113333)
samplingIntrl=60
MaxrunTime = 20000.0
DEBUG = False

# estimates (inputs to simulation) 
MeanThinkTime = 1.0 ## seconds
MeanCPUTime = 0.10    ## seconds

## inputs
numberOfUsers = [52,52]
numberOfServers= [16,4] 
maxServers=20

# outputs
completed=[] 
throughput=[]
utilization_agg=[]
utilization = []


cpu  = Resource(name='cpu', capacity=maxServers , monitored=True, qType=PriorityQ) # preemptable=True
initialize()
g = Generator('gen')
activate(g,g.execute(numberOfUsers=numberOfUsers, samplingIntrl=samplingIntrl))

simulate(until = MaxrunTime)

## result=main()

## Analysis/output -------------------------
# print 'centralserver'
# print '%7.4f: CPU rate = %7.4f tasks per second'%result
            

min=1;
acc=0;
t_old=0;
for item in cpu.actMon:
    t=item[0]
    util=item[1]    
    if t>min*samplingIntrl:        
        occupiers = maxServers-numberOfServers[min-1] # dont calculate occupiers in this model
        agg_util=acc/samplingIntrl-occupiers
        utilization_agg.append(agg_util) # numberOfServers[min-1]
        utilization.append(agg_util/numberOfServers[min-1])
        min+=1        
        acc=0
    acc+=(t-t_old)*util;      
    t_old=t
    
    
print utilization_agg         
print utilization
print throughput
# print cpu.actMon
print cpu.actMon.timeAverage()
print cpu.waitMon.timeAverage()
